/**
* Copyright (c) By zengqh.
*
* This program is just for fun or demo, in the hope that it  
* will be useful, you can redistribute it and/or modify freely.
*
* Time: 2013/05/26
* File: enn_gl_texture_obj.cpp
* Blog: http://www.cnblogs.com/zengqh/
**/

#include "enn_gl_texture_obj.h"
#include <enn_image.h>

namespace enn
{
TextureObj::TextureObj()
{
	memset(&tex_desc_, 0x0, sizeof(tex_desc_));
}

TextureObj::~TextureObj()
{
	destroyTexture();
}

TextureObj* TextureObj::createInstance()
{
	return new TextureObj;
}

void TextureObj::release()
{
	ENN_DELETE this;
}

void   TextureObj::createTexture()
{
	destroyTexture();
	glGenTextures( 1, &handle_ );
	checkGLError();
}

void   TextureObj::destroyTexture()
{
	if ( handle_ )
	{
		glDeleteTextures( 1, &handle_ );
		checkGLError();
		handle_ = 0;
	}
}

void TextureObj::createTexture2D(const TextureObj::TexDesc& tex_desc, const void* data)
{
	tex_desc_ = tex_desc;

	if (tex_desc_.is_mipmap_)
	{
		buildMipMapTexture(data);
	}
	else
	{
		buildTexture(data);
	}
}

void TextureObj::createTextureCube(const TextureObj::TexDesc& tex_desc, const void* data[6])
{
	tex_desc_ = tex_desc;
	buildCubeTexture(data);
}

void TextureObj::updateTexture(const void* data)
{
	glBindTexture( GL_TEXTURE_2D, handle_ );

	glTexSubImage2D( GL_TEXTURE_2D, 0, tex_desc_.internal_format_, 
		tex_desc_.width_, tex_desc_.height_, 0, tex_desc_.external_format_, 
		tex_desc_.type_, data );
	checkGLError();
}

void TextureObj::updateCubeTexture(int face, const void* data)
{
	glBindTexture( GL_TEXTURE_CUBE_MAP, handle_ );

	GLenum target = face + GL_TEXTURE_CUBE_MAP_POSITIVE_X;

	glTexSubImage2D( target, 0, tex_desc_.internal_format_, 
		tex_desc_.width_, tex_desc_.height_, 0, 
		tex_desc_.external_format_, tex_desc_.type_, data );
	
}

/** 2d texture */
void   TextureObj::buildTexture(const void* data)
{
	glBindTexture( GL_TEXTURE_2D, handle_ );

	glTexImage2D( GL_TEXTURE_2D, 0, tex_desc_.internal_format_, 
		tex_desc_.width_, tex_desc_.height_, 0, tex_desc_.external_format_, 
		tex_desc_.type_, data );

	checkGLError();

	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex_desc_.min_filter_ );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex_desc_.mag_filter_ );

	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex_desc_.wrap_s_ );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex_desc_.wrap_t_ );

	glBindTexture( GL_TEXTURE_2D, 0 );
	checkGLError();

	tex_desc_.texture_type_ = TEXTURE_2D;
}

void   TextureObj::buildMipMapTexture(const void* data)
{
	glBindTexture( GL_TEXTURE_2D, handle_ );

	gl.gluBuild2DMipmaps( GL_TEXTURE_2D, tex_desc_.internal_format_, 
		tex_desc_.width_, tex_desc_.height_, 0, tex_desc_.external_format_,
		tex_desc_.type_, data );
	checkGLError();

	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex_desc_.min_filter_ );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex_desc_.mag_filter_ );

	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, tex_desc_.wrap_s_ );
	glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, tex_desc_.wrap_t_ );

	glBindTexture( GL_TEXTURE_2D, 0 );
	checkGLError();
}

/** cube texture */
void TextureObj::buildCubeTexture( const void* data[6] )
{
	glBindTexture( GL_TEXTURE_CUBE_MAP, handle_ );

	for ( int i=0; i<6; ++i )
	{
		GLenum target = i+GL_TEXTURE_CUBE_MAP_POSITIVE_X;

		glTexImage2D( target, 0, tex_desc_.internal_format_, 
			tex_desc_.width_, tex_desc_.height_, 0, 
			tex_desc_.external_format_, tex_desc_.type_, data[i] );
	}

	glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, tex_desc_.min_filter_ );
	glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, tex_desc_.mag_filter_ );

	glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE );
	glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE );
	glTexParameteri( GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE );

	glBindTexture( GL_TEXTURE_CUBE_MAP, 0 );
	checkGLError();

	tex_desc_.texture_type_ = TEXTURE_CUBE;
}

bool TextureObj::load(Image* image)
{
	/** impl later. */
	return true;
}

bool TextureObj::load_cube(int face, Image* image)
{
	/** impl later. */
	return true;
}

bool TextureObj::save(Image* image)
{
	/** impl later. */
	return true;
}

bool TextureObj::save_cube(int face, Image* image)
{
	/** impl later. */
	return true;
}

}